/***************************************************************************
 *   Copyright (C) 2016 by Peter Shieh <peters996@gmail.com>               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/
#include "tlink_usb.h"

#define MAX_USB_IDS     2
/* vid = pid = 0 marks the end of the list */
static uint16_t tlink_vids[MAX_USB_IDS + 1] = {0x248a, 0x248a, 0 };
static uint16_t tlink_pids[MAX_USB_IDS + 1] = {0x8266, 0x5320, 0 };

#define EPT_BULK_OUT        0x05
#define EPT_BULK_IN         0x88

#define TLINK_IN_BUFFER_SIZE     (4*1024)
#define TLINK_OUT_BUFFER_SIZE    (4*1024)
#define TLINK_USB_BUFFER_SIZE    (64)

#define USB_INTERFACE		0

/* Global USB buffers */
#define USB_TIMEOUT        	4000
#define USB_MAX_BUFFER     	48  	// because if we happen to read 64 then next read will fail 

// for tcdb using control transfer
#define CTRL_OUT  (LIBUSB_ENDPOINT_OUT |  LIBUSB_REQUEST_TYPE_VENDOR | \
		  LIBUSB_RECIPIENT_INTERFACE  )

#define CTRL_IN   (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | \
		LIBUSB_RECIPIENT_INTERFACE )

#define USB_WRITE_REQUEST	0x02//usb spec
#define USB_WRITE_VALUE 	0x00 
#define USB_WRITE_INDEX 	0x00
#define USB_WRITE_CMD 		0x02//defined by telink

#define USB_READ_REQUEST   	0x02
#define USB_READ_INDEX 	   	0x00

libusb_device_handle  * tlink_dev = NULL;
static struct libusb_context *tlink_libusb_context; /**< Libusb context **/
static libusb_device **devs; /**< The usb device list **/


/* Global USB buffers */
static uint8_t usb_in_buffer[TLINK_IN_BUFFER_SIZE];
static uint8_t usb_out_buffer[TLINK_OUT_BUFFER_SIZE];

// forware
#ifdef TDEBUG
static void printdev(libusb_device *dev);
#endif 

void tlink_debug_buffer(const uint8_t *buffer, int length)
{
#define BYTES_PER_LINE  16
	char line[81]={0};
	char s[4];
	int i;
	int j;

	LOG_DEBUG("Debug USB buffer len = %d", length);
	for (i = 0; i < length; i += BYTES_PER_LINE) {
		snprintf(line, 5, "%04x", i);
		for (j = i; j < i + BYTES_PER_LINE && j < length; j++) {
			snprintf(s, 4, " %02x", buffer[j]);
			//strcat(line, s);
			memcpy(line+4+3*(j-i),s,3);
		}
		LOG_DEBUG("%s", line);
	}
}

libusb_device_handle  * tlink_usb_open()
{
	if (tlink_libusb_open(tlink_vids, tlink_pids, NULL, &tlink_dev) == LIBUSB_SUCCESS)
	{
		
		//LOG_DEBUG("tlink_dev  0x%8.8x", (uint32_t)tlink_dev);
	 	
		// Informational
		//if (libusb_kernel_driver_active(tlink_dev, USB_INTERFACE) == 1) { 
		//	LOG_DEBUG("Kernel Driver Active\n");
		//	if (libusb_detach_kernel_driver(tlink_dev, USB_INTERFACE) == 0) 
		//	LOG_DEBUG("Kernel Driver Detached!\n");
		//}
		//		
		//
		//if (libusb_claim_interface(tlink_dev, USB_INTERFACE) != LIBUSB_SUCCESS) {
		//	LOG_DEBUG("Opening Interface 1 failed, Please Insert Telink Debugger");
		//	tlink_usb_close();
		//	return NULL;
		//}
		return  tlink_dev;

	}

	return  NULL;
}

void tlink_usb_close()
{
	if(NULL!=tlink_dev){
	  LOG_DEBUG("tlink_usb_close");
	  tlink_libusb_close(tlink_dev);
	  tlink_dev = NULL;
	}
}


int tlink_usb_control_read(libusb_device_handle  * tlink, uint16_t deviceAddress, uint16_t length, 
		uint8_t *readBuffer, int * actual)
{
	uint16_t wValue = deviceAddress;
	uint16_t wLength = length + 8;

	LOG_DEBUG("tlink_usb_control_read Reading %d bytes", length);
	LOG_DEBUG("USB handle %8.8X", (uint32_t) tlink);

	int retval  = libusb_control_transfer(tlink, CTRL_IN, USB_READ_REQUEST, wValue, 
		USB_READ_INDEX, usb_in_buffer, wLength, USB_TIMEOUT);

	LOG_DEBUG("Reading %d bytes from %8.8x actual %d", length,deviceAddress,retval);

#ifdef TDEBUGUSB
    tlink_debug_buffer(usb_in_buffer, wLength);
#endif

	if (retval < 0) {
		LOG_DEBUG("Read error  %d\n", retval);
		return ERROR_FAIL;
	}

	// PS sizeof is definitely not right!!
	// strlen is a bad thing for data!!
	//if (retval  < (int)strlen((const char *)usb_in_buffer)) {
	if (retval  < length) {
		LOG_DEBUG("short read %d", retval);
		return ERROR_FAIL;
	}


	if (readBuffer) {
		memcpy(readBuffer, usb_in_buffer, length);
	}

	if (actual) {
		* actual  = retval;
	}

	return retval;
}


int tlink_usb_control_write(libusb_device_handle  * tlink, uint16_t deviceAddress, uint16_t length, uint8_t * writeBuffer) 
{
	usb_out_buffer[0] = USB_WRITE_CMD;
	usb_out_buffer[1] = deviceAddress >> 8;
	usb_out_buffer[2] = deviceAddress;
	usb_out_buffer[3] = usb_out_buffer[4] = usb_out_buffer[5] = usb_out_buffer[6] = usb_out_buffer[7] = writeBuffer[0];
 	memcpy (usb_out_buffer + 8, writeBuffer, length);

	// should be attached at the end
	int wLength = length + 8;
	usb_out_buffer[wLength] = '\0';

#ifdef TDEBUGUSB
	tlink_debug_buffer(usb_out_buffer, wLength);
#endif
	// returns: on success, the number of bytes actually transferred
	int retval  = libusb_control_transfer(tlink, CTRL_OUT,USB_WRITE_REQUEST,deviceAddress, 0, usb_out_buffer, wLength, USB_TIMEOUT);
        
	if (retval < 0) {
		LOG_DEBUG( "write to endpoint0 error %d\n", retval);
		return ERROR_FAIL;
	}

	if (retval  < 0) {
		LOG_DEBUG("short write (%d)\n", retval);
		return ERROR_FAIL;
	}

	return retval;
}

#ifdef TDEBUG

static void print_devs(libusb_device **devs)
{
	libusb_device *dev;
	int i = 0;
	while ((dev = devs[i++]) != NULL) {
		struct libusb_device_descriptor desc;
		int r = libusb_get_device_descriptor(dev, &desc);
		if (r < 0) {
			fprintf(stderr, "failed to get device descriptor");
			return;
		}
		printf("%04x:%04x (bus %d, device %d)\n",
			desc.idVendor, desc.idProduct,
			libusb_get_bus_number(dev), libusb_get_device_address(dev));
	}
}


void printdev(libusb_device *dev) 
{
 	struct libusb_device_descriptor desc;
	
    int r = libusb_get_device_descriptor(dev, &desc);
	
    if (r < 0) {
	        LOG_DEBUG("failed to get device descriptor");
        return;
	} 
    LOG_DEBUG("Number of possible configurations: %d", desc.bNumConfigurations);
    LOG_DEBUG("Device Class: %d", (int)desc.bDeviceClass );
    LOG_DEBUG("Device SubClass: %d", (int)desc.bDeviceSubClass );
    LOG_DEBUG("VendorID:  0x%4X", desc.idVendor);
    LOG_DEBUG("ProductID: 0x%4X", desc.idProduct);
 

	struct libusb_config_descriptor *config;
    r = libusb_get_config_descriptor(dev, 0, &config);
	 
    if(r<0) return;
	LOG_DEBUG("Total Interfaces: %d", (int)config->bNumInterfaces);

			
    const struct libusb_interface *inter;
    const struct libusb_interface_descriptor *interdesc;
    const struct libusb_endpoint_descriptor *epdesc;

    for(int i=0; i<(int)config->bNumInterfaces; i++) {

        inter = &config->interface[i];
        LOG_DEBUG("Number of alternate settings: %d ", inter->num_altsetting);


        for(int j=0; j<inter->num_altsetting; j++) {

            interdesc = &inter->altsetting[j];
           	LOG_DEBUG("Interface Number: %d ", (int)interdesc->bInterfaceNumber);
            LOG_DEBUG("Number of endpoints: %d", (int)interdesc->bNumEndpoints);
            LOG_DEBUG("Interface Class: %d %s", (int)interdesc->bInterfaceClass, 
						interdesc->bInterfaceClass == LIBUSB_CLASS_PRINTER ? "Printer Class" : "");
					
		    for(int k=0; k<(int)interdesc->bNumEndpoints; k++) { 
				epdesc = &interdesc->endpoint[k];
				LOG_DEBUG("Descriptor Type: %d ", (int)epdesc->bDescriptorType);
				LOG_DEBUG("EP Address: 0x%8.8x", epdesc->bEndpointAddress);
      		}
    	}
	}
	
	libusb_free_config_descriptor(config);

}
#endif 


/* Returns true if the string descriptor indexed by str_index in device matches string */
static bool string_descriptor_equal(libusb_device_handle *device, uint8_t str_index,
									const char *string)
{
	int retval;
	bool matched;
	char desc_string[256+1]; /* Max size of string descriptor */
    LOG_DEBUG("--libusb_get_string_descriptor_ascii");
	if (str_index == 0)
		return false;

	retval = libusb_get_string_descriptor_ascii(device, str_index,
			(unsigned char *)desc_string, sizeof(desc_string)-1);
	if (retval < 0) {
		LOG_DEBUG("libusb_get_string_descriptor_ascii() failed with %d", retval);
		return false;
	}

	/* Null terminate descriptor string in case it needs to be logged. */
	desc_string[sizeof(desc_string)-1] = '\0';

	matched = strncmp(string, desc_string, sizeof(desc_string)) == 0;
	if (!matched)
		LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'",
			desc_string, string);
	return matched;
}

#if 1

static int perr(char const *format, ...)
{
	va_list args;
	int r;

	va_start (args, format);
	r = vfprintf(stderr, format, args);
	va_end(args);

	return r;
}


static void display_buffer_hex(unsigned char *buffer, unsigned size)
{
	unsigned i, j, k;

	for (i=0; i<size; i+=16) {
		printf("\n  %08x  ", i);
		for(j=0,k=0; k<16; j++,k++) {
			if (i+j < size) {
				printf("%02x", buffer[i+j]);
			} else {
				printf("  ");
			}
			printf(" ");
		}
		printf(" ");
		for(j=0,k=0; k<16; j++,k++) {
			if (i+j < size) {
				if ((buffer[i+j] < 32) || (buffer[i+j] > 126)) {
					printf(".");
				} else {
					printf("%c", buffer[i+j]);
				}
			}
		}
	}
	printf("\n" );
}

#define ERR_EXIT(errcode) do { perr("   %s\n", libusb_strerror((enum libusb_error)errcode)); return -1; } while (0)
#define CALL_CHECK(fcall) do { r=fcall; if (r < 0) ERR_EXIT(r); } while (0);
#define B(x) (((x)!=0)?1:0)
#define be_to_int32(buf) (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|(buf)[3])

#define RETRY_MAX                     5
#define REQUEST_SENSE_LENGTH          0x12
#define INQUIRY_LENGTH                0x24
#define READ_CAPACITY_LENGTH          0x08

// Future versions of libusbx will use usb_interface instead of interface
// in libusb_config_descriptor => catter for that
#define usb_interface interface

// Read the MS WinUSB Feature Descriptors, that are used on Windows 8 for automated driver installation
static void read_ms_winsub_feature_descriptors(libusb_device_handle *handle, uint8_t bRequest, int iface_number)
{
#define MAX_OS_FD_LENGTH 256
	int i, r;
	uint8_t os_desc[MAX_OS_FD_LENGTH];
	uint32_t length;
	void* le_type_punning_IS_fine;
	struct {
		const char* desc;
		uint8_t recipient;
		uint16_t index;
		uint16_t header_size;
	} os_fd[2] = {
		{"Extended Compat ID", LIBUSB_RECIPIENT_DEVICE, 0x0004, 0x10},
		{"Extended Properties", LIBUSB_RECIPIENT_INTERFACE, 0x0005, 0x0A}
	};

	if (iface_number < 0) return;
	// WinUSB has a limitation that forces wIndex to the interface number when issuing
	// an Interface Request. To work around that, we can force a Device Request for
	// the Extended Properties, assuming the device answers both equally.
	//if (force_device_request)
		os_fd[1].recipient = LIBUSB_RECIPIENT_DEVICE;

	for (i=0; i<2; i++) {
		printf("\nReading %s OS Feature Descriptor (wIndex = 0x%04d):\n", os_fd[i].desc, os_fd[i].index);

		// Read the header part
		r = libusb_control_transfer(handle, (uint8_t)(LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|os_fd[i].recipient),
			bRequest, (uint16_t)(((iface_number)<< 8)|0x00), os_fd[i].index, os_desc, os_fd[i].header_size, 1000);
		if (r < os_fd[i].header_size) {
			perr("   Failed: %s", (r<0)?libusb_strerror((enum libusb_error)r):"header size is too small");
			return;
		}
		le_type_punning_IS_fine = (void*)os_desc;
		length = *((uint32_t*)le_type_punning_IS_fine);
		if (length > MAX_OS_FD_LENGTH) {
			length = MAX_OS_FD_LENGTH;
		}

		// Read the full feature descriptor
		r = libusb_control_transfer(handle, (uint8_t)(LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|os_fd[i].recipient),
			bRequest, (uint16_t)(((iface_number)<< 8)|0x00), os_fd[i].index, os_desc, (uint16_t)length, 1000);
		if (r < 0) {
			perr("   Failed: %s", libusb_strerror((enum libusb_error)r));
			return;
		} else {
			display_buffer_hex(os_desc, r);
		}
	}
}


static char* uuid_to_string(const uint8_t* uuid)
{
	static char uuid_string[40];
	if (uuid == NULL) return NULL;
	sprintf(uuid_string, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
		uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
		uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
	return uuid_string;
}

static void print_device_cap(struct libusb_bos_dev_capability_descriptor *dev_cap)
{
	switch(dev_cap->bDevCapabilityType) {
	case LIBUSB_BT_USB_2_0_EXTENSION: {
		struct libusb_usb_2_0_extension_descriptor *usb_2_0_ext = NULL;
		libusb_get_usb_2_0_extension_descriptor(NULL, dev_cap, &usb_2_0_ext);
		if (usb_2_0_ext) {
			printf("    USB 2.0 extension:\n");
			printf("      attributes             : %02X\n", usb_2_0_ext->bmAttributes);
			libusb_free_usb_2_0_extension_descriptor(usb_2_0_ext);
		}
		break;
	}
	case LIBUSB_BT_SS_USB_DEVICE_CAPABILITY: {
		struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap = NULL;
		libusb_get_ss_usb_device_capability_descriptor(NULL, dev_cap, &ss_usb_device_cap);
		if (ss_usb_device_cap) {
			printf("    USB 3.0 capabilities:\n");
			printf("      attributes             : %02X\n", ss_usb_device_cap->bmAttributes);
			printf("      supported speeds       : %04X\n", ss_usb_device_cap->wSpeedSupported);
			printf("      supported functionality: %02X\n", ss_usb_device_cap->bFunctionalitySupport);
			libusb_free_ss_usb_device_capability_descriptor(ss_usb_device_cap);
		}
		break;
	}
	case LIBUSB_BT_CONTAINER_ID: {
		struct libusb_container_id_descriptor *container_id = NULL;
		libusb_get_container_id_descriptor(NULL, dev_cap, &container_id);
		if (container_id) {
			printf("    Container ID:\n      %s\n", uuid_to_string(container_id->ContainerID));
			libusb_free_container_id_descriptor(container_id);
		}
		break;
	}
	default:
		printf("    Unknown BOS device capability %02x:\n", dev_cap->bDevCapabilityType);
	}	
}
 

int tlink_libusb_open(const uint16_t vids[], const uint16_t pids[],
		const char *serial, struct libusb_device_handle **out)
{
    libusb_device_handle *handle;
	libusb_device *dev;
	uint8_t bus, port_path[8];
	struct libusb_bos_descriptor *bos_desc;
	struct libusb_config_descriptor *conf_desc;
	const struct libusb_endpoint_descriptor *endpoint;
	int i, j, k, r;
	int iface, nb_ifaces, first_iface = -1;
	struct libusb_device_descriptor dev_desc;
	const char* speed_name[5] = { "Unknown", "1.5 Mbit/s (USB LowSpeed)", "12 Mbit/s (USB FullSpeed)",
		"480 Mbit/s (USB HighSpeed)", "5000 Mbit/s (USB SuperSpeed)"};
	char string[128];
	uint8_t string_index[3];	// indexes of the string descriptors
	uint8_t endpoint_in = 0, endpoint_out = 0;	// default IN and OUT endpoints
	
	if ((libusb_init(NULL)) < 0) {		
		return -1;
	}
 
    for(i=0;i<MAX_USB_IDS+1;i++){
	  handle = libusb_open_device_with_vid_pid(NULL, vids[i], pids[i]);
      
	  if (handle != NULL) {
	  	break;
	  }
	}  
	if(handle == NULL)
	  return -1;

	dev = libusb_get_device(handle);
	bus = libusb_get_bus_number(dev);
	if (0) {
		r = libusb_get_port_numbers(dev, port_path, sizeof(port_path));
		if (r > 0) {
			printf("\nDevice properties:\n");
			printf("        bus number: %d\n", bus);
			printf("         port path: %d", port_path[0]);
			for (i=1; i<r; i++) {
				printf("->%d", port_path[i]);
			}
			printf(" (from root hub)\n");
		}
		r = libusb_get_device_speed(dev);
		if ((r<0) || (r>4)) r=0;
		printf("             speed: %s\n", speed_name[r]);
	}

#ifdef TDEBUGUSB	
	printf("\nReading device descriptor:\n");
	CALL_CHECK(libusb_get_device_descriptor(dev, &dev_desc));
	printf("            length: %d\n", dev_desc.bLength);
	printf("      device class: %d\n", dev_desc.bDeviceClass);
	printf("               S/N: %d\n", dev_desc.iSerialNumber);
	printf("           VID:PID: %04X:%04X\n", dev_desc.idVendor, dev_desc.idProduct);
	printf("         bcdDevice: %04X\n", dev_desc.bcdDevice);
	printf("   iMan:iProd:iSer: %d:%d:%d\n", dev_desc.iManufacturer, dev_desc.iProduct, dev_desc.iSerialNumber);
	printf("          nb confs: %d\n", dev_desc.bNumConfigurations);

	// Copy the string descriptors for easier parsing
	string_index[0] = dev_desc.iManufacturer;
	string_index[1] = dev_desc.iProduct;
	string_index[2] = dev_desc.iSerialNumber;

	printf("\nReading BOS descriptor: ");
	if (libusb_get_bos_descriptor(handle, &bos_desc) == LIBUSB_SUCCESS) {
		printf("%d caps\n", bos_desc->bNumDeviceCaps);
		for (i = 0; i < bos_desc->bNumDeviceCaps; i++)
			print_device_cap(bos_desc->dev_capability[i]);
		libusb_free_bos_descriptor(bos_desc);
	} else {
		printf("no descriptor\n");
	}

	printf("\nReading first configuration descriptor:\n");
	CALL_CHECK(libusb_get_config_descriptor(dev, 0, &conf_desc));
	nb_ifaces = conf_desc->bNumInterfaces;
	printf("             nb interfaces: %d\n", nb_ifaces);
	if (nb_ifaces > 0)
		first_iface = conf_desc->usb_interface[0].altsetting[0].bInterfaceNumber;
	for (i=0; i<nb_ifaces; i++) {
		printf("              interface[%d]: id = %d\n", i,
			conf_desc->usb_interface[i].altsetting[0].bInterfaceNumber);
		for (j=0; j<conf_desc->usb_interface[i].num_altsetting; j++) {
			printf("interface[%d].altsetting[%d]: num endpoints = %d\n",
				i, j, conf_desc->usb_interface[i].altsetting[j].bNumEndpoints);
			printf("   Class.SubClass.Protocol: %02X.%02X.%02X\n",
				conf_desc->usb_interface[i].altsetting[j].bInterfaceClass,
				conf_desc->usb_interface[i].altsetting[j].bInterfaceSubClass,
				conf_desc->usb_interface[i].altsetting[j].bInterfaceProtocol);
			
			for (k=0; k<conf_desc->usb_interface[i].altsetting[j].bNumEndpoints; k++) {
				struct libusb_ss_endpoint_companion_descriptor *ep_comp = NULL;
				endpoint = &conf_desc->usb_interface[i].altsetting[j].endpoint[k];
				printf("       endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress);
				// Use the first interrupt or bulk IN/OUT endpoints as default for testing
				if ((endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) & (LIBUSB_TRANSFER_TYPE_BULK | LIBUSB_TRANSFER_TYPE_INTERRUPT)) {
					if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
						if (!endpoint_in)
							endpoint_in = endpoint->bEndpointAddress;
					} else {
						if (!endpoint_out)
							endpoint_out = endpoint->bEndpointAddress;
					}
				}
				printf("           max packet size: %04X\n", endpoint->wMaxPacketSize);
				printf("          polling interval: %02X\n", endpoint->bInterval);
				libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp);
				if (ep_comp) {
					printf("                 max burst: %02X   (USB 3.0)\n", ep_comp->bMaxBurst);
					printf("        bytes per interval: %04X (USB 3.0)\n", ep_comp->wBytesPerInterval);
					libusb_free_ss_endpoint_companion_descriptor(ep_comp);
				}
			}
		}
	}
	libusb_free_config_descriptor(conf_desc);
#endif	
	libusb_set_auto_detach_kernel_driver(handle, 1);
	for (iface = 0; iface < nb_ifaces; iface++)
	{
		printf("\nClaiming interface %d...\n", iface);
		r = libusb_claim_interface(handle, iface);
		if (r != LIBUSB_SUCCESS) {
			perr("interface %d  Failed.\n",iface);
		}
	}
#ifdef TDEBUGUSB	
	printf("\nReading string descriptors:\n");
	for (i=0; i<3; i++) {
		if (string_index[i] == 0) {
			continue;
		}
		if (libusb_get_string_descriptor_ascii(handle, string_index[i], (unsigned char*)string, 128) >= 0) {
			printf("   String (0x%02X): \"%s\"\n", string_index[i], string);
		}
	}
	// Read the OS String Descriptor
	if (libusb_get_string_descriptor_ascii(handle, 0xEE, (unsigned char*)string, 128) >= 0) {
		printf("   String (0x%02X): \"%s\"\n", 0xEE, string);
		// If this is a Microsoft OS String Descriptor,
		// attempt to read the WinUSB extended Feature Descriptors
		if (strncmp(string, "MSFT100", 7) == 0)
			read_ms_winsub_feature_descriptors(handle, string[7], first_iface);
	}
 
 #endif	

	//for (iface = 0; iface<nb_ifaces; iface++) {
	//	printf("Releasing interface %d...\n", iface);
	//	libusb_release_interface(handle, iface);
	//}

	*out = handle;
	
	return 0;
}
#else
int tlink_libusb_open(const uint16_t vids[], const uint16_t pids[],
		const char *serial, struct libusb_device_handle **out)
{
	int cnt, idx;
	int retval;
	struct libusb_device_handle *libusb_handle = NULL;

	LOG_DEBUG("-");

	//if ((retval = libusb_init(&tlink_libusb_context)) < 0) {
	if ((retval = libusb_init(NULL)) < 0) {
		LOG_DEBUG("libusb_init failed with %d", retval);
		return ERROR_FAIL;
	}

	//libusb_set_debug(tlink_libusb_context, 3);
	libusb_set_debug(NULL, 3);

	//cnt = libusb_get_device_list(tlink_libusb_context, &devs);
	cnt = libusb_get_device_list(NULL, &devs);

	if (cnt < 0)
		return ERROR_FAIL;

	for (idx = 0; idx < cnt; idx++) {

		struct libusb_device_descriptor dev_desc;

		retval = libusb_get_device_descriptor(devs[idx], &dev_desc);
	//	assert(retval == 0);

		LOG_DEBUG("VID 0x%04x PID 0x%04x", dev_desc.idVendor, dev_desc.idProduct);

		for (unsigned i = 0; vids[i]; i++) {

			//libusb_handle  = libusb_open_device_with_vid_pid(tlink_libusb_context, vids[i], pids[i]);
			libusb_handle  = libusb_open_device_with_vid_pid(NULL, vids[i], pids[i]);

		 	if (libusb_handle != NULL) {
				LOG_DEBUG("found dev vid 0x%04x pid 0x%04x", vids[i], pids[i]);
#ifdef TDEBUG
				//printdev(devs[idx]);
				print_devs(devs);
#endif
				if (serial != NULL && !string_descriptor_equal(libusb_handle, dev_desc.iSerialNumber, serial)) {
					LOG_USER("Serial number not matched");
					libusb_close(libusb_handle);
					libusb_free_device_list(devs, 1);
					return ERROR_FAIL;
				}
				*out = libusb_handle;
				libusb_free_device_list(devs, 1);
				return ERROR_OK;
			}
		}
	}

	LOG_DEBUG("device not found");

	return ERROR_FAIL;
}

#endif

void tlink_libusb_close(libusb_device_handle *dev)
{    
	
	LOG_DEBUG("tlink_libusb_close");
	if (dev){
 		libusb_release_interface(dev, USB_INTERFACE);
		/* Close device */
		libusb_close(dev);
	}

	//libusb_exit(tlink_libusb_context);
	libusb_exit(NULL);
}

int tlink_libusb_control_transfer(libusb_device_handle *dev, uint8_t requestType,
		uint8_t request, uint16_t wValue, uint16_t wIndex, char *bytes,
		uint16_t size, unsigned int timeout)
{
	int transferred = 0;

	transferred = libusb_control_transfer(dev, requestType, request, wValue, wIndex,
				(unsigned char *)bytes, size, timeout);

	if (transferred < 0)
		transferred = 0;

	return transferred;
}




